#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>

class Sock
{
private:
    // listen的第二个参数，意义：底层全连接队列的长度 = listen的第二个参数+1
    const static int gbacklog = 10;
public:
    Sock() {}
    static int Socket()
    {
        int listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock < 0)
        {
            exit(2);
        }
        int opt = 1;
        setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        return listensock;
    }
    static void Bind(int sock, uint16_t port, std::string ip = "0.0.0.0")
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &local.sin_addr);
        if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            exit(3);
        }
    }
    static void Listen(int sock)
    {
        if (listen(sock, gbacklog) < 0)
        {
            exit(4);
        }

    }
    // 一般经验
    // const std::string &: 输入型参数
    // std::string *: 输出型参数
    // std::string &: 输入输出型参数
    static int Accept(int listensock, std::string *ip, uint16_t *port)
    {
        struct sockaddr_in src;
        socklen_t len = sizeof(src);
        int servicesock = accept(listensock, (struct sockaddr *)&src, &len);
        if (servicesock < 0)
        {
            return -1;
        }
        if(port) *port = ntohs(src.sin_port);
        if(ip) *ip = inet_ntoa(src.sin_addr);
        return servicesock;
    }
    static bool Connect(int sock, const std::string &server_ip, const uint16_t &server_port)
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(server_port);
        server.sin_addr.s_addr = inet_addr(server_ip.c_str());

        if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0) return true;
        else return false;
    }
    ~Sock() {}
};


/*
第二段代码与第一段相比，主要做了以下修改：

gbacklog值的修改：将原来的20改为了10，这是服务器监听套接字的全连接队列的长度减1后的值。

增加socket选项设置：在Socket()方法中增加了设置socket选项的代码，设置了SO_REUSEADDR和SO_REUSEPORT，以允许地址复用，这样同一端口上的多个绑定请求可以复用同一个地址。

错误处理方式的修改：在第二段代码中，当遇到socket、bind、listen等函数调用失败时，直接调用了exit()函数，而没有像第一段代码那样通过logMessage打印错误日志再退出。这意味着第二段代码移除了对日志系统的依赖。

静态方法的声明和定义：第二段代码中的Socket、Bind、Listen、Accept和Connect方法都被声明为static，这意味着它们不再依赖于类实例，可以直接通过类名调用。这改变了类的设计，使之更像是一个工具类，不需实例化即可使用其提供的网络功能。

删除了logMessage函数调用：第二段代码删除了所有对logMessage函数的调用，这意味着所有错误信息将不会被记录到日志中，而是直接导致程序退出。

设置SO_REUSEADDR和SO_REUSEPORT这两个套接字选项的主要目的是为了提高服务端程序的可用性和灵活性：

SO_REUSEADDR：

当设置此选项后，即使某个TCP连接未完全关闭（例如，服务进程意外终止，但TIME_WAIT状态还未超时），新启动的服务进程也可以立即绑定到相同的IP地址和端口号。
在多进程或多线程模型中，可以让新的服务进程快速重启并接管客户端的连接请求，减少因端口占用而导致的服务不可用时间。
SO_REUSEPORT：

此选项主要用于Linux内核，允许多个套接字同时绑定到同一个端口上，每个套接字由不同的进程拥有。
当开启这个选项后，操作系统内核会负载均衡地分发来自不同客户端的连接请求到这些已经绑定到同一端口的不同套接字（即不同进程）上。
这样做可以有效地提升服务端的并发处理能力，特别是在高并发场景下，能够更好地利用多核CPU资源。
总之，这两个选项的设置对于服务端程序来说，有助于提高其可靠性、并发性能以及在异常情况下的快速恢复能力。但在实际应用中，特别是涉及到安全和可靠性的场景时，需要根据具体情况权衡是否启用这些选项。
*/